#pragma once
#include <string.h>
#include <stdio.h>

#undef EOF
static const int EOF            = -1;
static const int EXIT_ON_ERROR  =  0;
static const int PRINT_WARNINGS =  1;

//class Hash{};
typedef int Hash;

//class Key{};
typedef long long Key;

enum TType{
    TOK_NO_TYPE,
    TOK_PLUS,
    TOK_MINUS,
    TOK_SLASH,
    TOK_STAR,
    TOK_ANGLE_BR_OPEN,
    TOK_ANGLE_BR_CLOSE,
    TOK_ASSIGN,
    TOK_EXCL,
    TOK_AMPERSAND,
    TOK_SEMICOLON,
    TOK_BR_OPEN,
    TOK_BR_CLOSE,
    TOK_CURLY_BR_OPEN,
    TOK_CURLY_BR_CLOSE,
    TOK_SQ_BR_OPEN,
    TOK_SQ_BR_CLOSE,
    TOK_SPECIAL,
    TOK_INTEGER,
    TOK_IDENTIFIER,
    TOK_COMMENT_OPEN,
    TOK_COMMENT_CLOSE,
    //Added in Task 2
    TOK_COLON,
    TOK_PRINT,
    TOK_READ,
    TOK_IF,
    TOK_ELSE,
    TOK_WHILE,
    TOK_INT,
    TOK_NO_TOK,
    TOK_INVALID,
    //Added in Task 2 for TypeCheck()
    TYPE_INVALID_TYPE,
    TYPE_INT,
    TYPE_INT_ARRAY,
    TYPE_ARRAY,
    TYPE_NO,
    TYPE_ERROR,
    TYPE_OP_PLUS,
    TYPE_OP_MINUS,
    TYPE_OP_MULT,
    TYPE_OP_DIV,
    TYPE_OP_LESS,
    TYPE_OP_GREATER,
    TYPE_OP_EQUAL,
    TYPE_OP_UNEQUAL,
    TYPE_OP_AND
};

class Information{
public:
    //declare members pubilc for convenience
    char* lexem;
    enum TType type;


    Information(char* lexem, TType type) :
        lexem(lexem),
        type(type)
    {
    }
};

class Lexem{

    char* chars;

    //the length of the string, including the trailing '\0'
    size_t length;

public:
    Lexem(char* chars, size_t len) :
      chars(chars),
      length(len)
    {
    }

    char* getChars()
    {
        return chars;
    }

    size_t getLength()
    {
        return length;
    }
};


union TokenData{
    Key asKey; //for TOK_IDENT
    long long asInt; //for TOK_INT
    char asChar; //for "TOK_SIGN"
};

class Token{
    TType type;
    unsigned int row;
    unsigned int col;
    TokenData data;

public:

    Token(TType type, unsigned int row, unsigned int col, TokenData data) :
        type(type),
        row(row),
        col(col),
        data(data)
    {
    }

             TType getType()           { return type;         }
    unsigned int   getRow ()           { return row;          }
    unsigned int    getCol()           { return col;          }
             Key    getKey()           { return data.asKey;   }
       long long    getInt()           { return data.asInt;   }
            char   getChar()           { return data.asChar;  }
    //Task II
             void  setType(TType type) { this->type = type;   }
    
};



class Helper
{
public:
    static bool isNumerical(char c)
    {
        if('0' <= c && c <= '9')
            return true;
        return false;
    }

    static bool isAlpha(char c)
    {
        if(('a' <= c && c <= 'z') ||
          ( 'A' <= c && c <= 'Z'))
            return true;
        return false;
    }

    static bool isSign(char c)
    {
        switch(c)
        {
            case '+':
            case '-':
            case '/':
            case '*':
            case '<':
            case '>':
            case '=':
            //case '<!>':
            case '!':
            case '&':
            case ';':
            case '(':
            case ')':
            case '{':
            case '}':
            case '[':
            case ']':
                return true;
        }

        return false;
    }

    static bool isSpace(char c)
    {
        if(c == ' ')
            return true;
        if(c == '\t')
            return true;
        if(c == '\n')//CR for Windows operating systems only
            return true;
        if(c == '\r')
            return true;

        return false;
    }
};
